﻿using Microsoft.EntityFrameworkCore;
using MyBlog2.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace MyBlog2.Repository
{
    public class CommentRepository : ICommentRepository
    {
        private readonly AppDbContext _context;

        public CommentRepository(AppDbContext context)
        {
            this._context = context;
        }

        /// <summary>
        /// 添加评论
        /// </summary>
        /// <param name="comment"></param>
        /// <returns></returns>
        public Comment AddComment(Comment comment)
        {
            //评论对应文章是否存在
            var article = _context.Articles.FirstOrDefault(a=>a.Id == comment.ArticleId);
            if (article == null)
            {
                return null;
            }

            //回复的父评论对象
            var pcom = GetOneComment(comment.ParentComment.Id);

            //评论新实例
            var com = new Comment
            {
                ArticleId = comment.ArticleId,
                Nickname = comment.Nickname,
                Email = comment.Email,
                Contents = comment.Contents,
                AddTime = DateTime.Now,
                ParentComment = pcom,
                //ReplyComments = comment.ReplyComments,
                AdminComment = comment.AdminComment,
            };
            _context.ArticleComments.Add(com);
            _context.SaveChanges();
            return com;
        }


        /// <summary>
        /// 删除评论
        /// </summary>
        /// <param name="id"></param>
        public void DeleteComment(int id)
        {
            var com = _context.ArticleComments.Find(id);
            if (com != null)
            {
                _context.ArticleComments.Remove(com);
                _context.SaveChanges();
            }
        }

        /// <summary>
        /// 获取评论集合
        /// </summary>
        /// <returns></returns>
        public List<Comment> GetCommentList(int articleId)
        {
            //顶级评论集合
            var comments = _context.ArticleComments.Include(c=>c.ParentComment).Include(c=>c.ReplyComments).ThenInclude(c => c.ReplyComments).Where(c=>c.ArticleId == articleId && c.ParentComment == null).OrderByDescending(c => c.AddTime).ToList();
            return eachTopComment(comments);    //遍历顶级评论集合
        }


        /// <summary>
        /// 遍历顶部评论集合，并合并子代
        /// </summary>
        /// <param name="comments"></param>
        /// <returns></returns>
        public List<Comment> eachTopComment(List<Comment> comments)
        {
            List<Comment> topList = new List<Comment>();
            foreach (var com in comments)
            {
                //复制com对象到c对象
                Comment c = TransExpV2<Comment, Comment>.Trans(com);
                topList.Add(c);
            }
            CombineChild(topList);
            return topList;
        }


        /// <summary>
        /// 复制对象（表达式树+反射+泛型）
        /// </summary>
        /// <typeparam name="TIn">输入泛型</typeparam>
        /// <typeparam name="TOut">输出泛型</typeparam>
        public static class TransExpV2<TIn, TOut>
        {

            private static readonly Func<TIn, TOut> cache = GetFunc();
            private static Func<TIn, TOut> GetFunc()
            {
                ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                List<MemberBinding> memberBindingList = new List<MemberBinding>();

                foreach (var item in typeof(TOut).GetProperties())
                {
                    if (!item.CanWrite)
                        continue;

                    MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                    MemberBinding memberBinding = Expression.Bind(item, property);
                    memberBindingList.Add(memberBinding);
                }

                MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });

                return lambda.Compile();
            }

            public static TOut Trans(TIn tIn)
            {
                return cache(tIn);
            }

        }


        /// <summary>
        /// 合并子代评论
        /// </summary>
        /// <param name="comments"></param>
        public void CombineChild(List<Comment> comments)
        {
            //遍历顶级评论
            foreach (var comment in comments)
            {
                //if (comment.ReplyComments == null)
                //{
                //    continue;
                //}
                List<Comment> replys = comment.ReplyComments.ToList();  //第二代评论集合
                foreach (var reply in replys)   //遍历第二代评论集合
                {
                    //递归二代
                    Recursive(reply);
                }
                //设置某顶级元素的回复集合为合并后的集合
                comment.ReplyComments = tempList;
                tempList = new List<Comment>();
            }
        }

        private List<Comment> tempList = new List<Comment>();   //暂存集合
        /// <summary>
        /// 递归函数，合并二代及以下代，到暂存集合
        /// </summary>
        /// <param name="reply"></param>
        private void Recursive(Comment comment)
        {
            tempList.Add(comment);  //二代，添加到暂存集合
            if (comment.ReplyComments.Count>0)  //二代有三代集合
            {
                List<Comment> replys = comment.ReplyComments.ToList();  //三代集合
                foreach (var reply in replys)   //遍历三代集合
                {
                    tempList.Add(reply);    //添加三代到暂存
                    if (reply.ReplyComments.Count>0)    //三代有四代
                    {
                        Recursive(reply);   //递归三代
                    }
                }
            }
        }

        

        /// <summary>
        /// 获取一个评论对象
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Comment GetOneComment(int id)
        {
            return _context.ArticleComments.FirstOrDefault(c=>c.Id == id);
        }
    }
}
